package com.broada.apm.util;

import java.io.IOException;
import java.util.Date;
import java.util.Map.Entry;
import java.util.Set;

import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider;
import org.codehaus.jackson.map.DeserializationConfig.Feature;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.map.ser.CustomSerializerFactory;

import com.broada.apm.model.performance.BasePerformance;

public class ApmJsonProvider extends JacksonJaxbJsonProvider {
    
	public ApmJsonProvider() {
		this.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
		ObjectMapper mapper = new ObjectMapper();
		CustomSerializerFactory customSerializerFactory = new CustomSerializerFactory() {
			{
				_concrete.put(Double.TYPE.getName(), new ApmDoubleSerializer());
				_concrete.put(Double.class.getName(), new ApmDoubleSerializer());
			}
		};
		customSerializerFactory.addGenericMapping(BasePerformance.class, new ApmPerformanceSerializer());
		mapper.setSerializerFactory(customSerializerFactory);
		this.setMapper(mapper);
	}
	
	public static class ApmPerformanceSerializer extends JsonSerializer<BasePerformance> {

		@Override
		public void serialize(BasePerformance value, JsonGenerator jgen, SerializerProvider provider) throws IOException,
				JsonProcessingException {
			jgen.writeStartObject();
			// "id":null,"timestamp":1427691120000,
			// "dimensionType":null,"dimensionId":null,
			// "applicationId":null,"serviceId":null,"resourceId":null,"key":false,
			// "tx":0,"rx":0,"flow":0,"request":0,"response":0,"error":0,"successRate":0.0,
			// "responseTime":0,"connections":0,"concurrentUsers":0,"concurrentConnections":0,
			// "uv":0,"apdex":0,"satisfied":0,"tolerating":0,"status":0,"protocol":-1
			writePerformance(value, jgen);
			jgen.writeEndObject();
		}
	}


	public static class ApmDoubleSerializer extends JsonSerializer<Double> {
		@Override
		public void serialize(Double value, JsonGenerator jgen, SerializerProvider provider) throws IOException,
				JsonProcessingException {
			jgen.writeNumber(Math.round(value * 100.0) / 100.0);
		}
	}

	public static <T extends BasePerformance> void writePerformance(T value, JsonGenerator jgen) throws IOException {
	    Set<Entry<String, Object>> entrySet = value.entrySet();
        for (Entry<String, Object> entry : entrySet) {
            if("successRate".equals(entry.getKey())){
                double nv = ((Number)entry.getValue()).doubleValue();
                jgen.writeNumberField("successRate", Math.round(nv * 100.0) / 100.0);
            }/* else if("responseTime".equals(entry.getKey())){
                long nv = ((Number)entry.getValue()).longValue();
                jgen.writeNumberField("responseTime", nv < 1000 ? nv / 1000.0 : nv / 1000);
            }*/ else if("timestamp".equals(entry.getKey())) {
                Date timestamp = value.getTimestamp();
                jgen.writeNumberField("timestamp", timestamp != null ? timestamp.getTime() : 0);
            } else if("system/score/apdex".equals(entry.getKey()) || "business/score/apdex".equals(entry.getKey())) {
            	long nv = ((Number)entry.getValue()).longValue();
            	double apdex = nv / 100.0;
            	
            	if ("system/score/apdex".equals(entry.getKey())) {
            		jgen.writeNumberField("system/score/apdex", apdex < 1 ? apdex : 1);//1.0->1
            	}
            	if ("business/score/apdex".equals(entry.getKey())) {
            		jgen.writeNumberField("business/score/apdex", apdex < 1 ? apdex : 1);//1.0->1
            	}
            } else {
                jgen.writeObjectField(entry.getKey(), entry.getValue());
            }
        }
        if(value.containsKey("response") && value.containsKey("satisfied") && value.containsKey("tolerating")){
            long disappointed = value.getLong("response") - value.getLong("satisfied") - value.getLong("tolerating");
            jgen.writeNumberField("disappointed", Math.max(disappointed, 0));
        }
		jgen.writeObjectField("statusMap", value.getStatusMap());
	}
}
